/**
 * Copyright (c) 2009 Jim Hurne
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
package org.tzconverter.tzdisplay;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.ParseException;
import java.util.Date;
import java.util.TimeZone;

import org.tzconverter.AbstractManager;
import org.tzconverter.Converter;
import org.tzconverter.menu.MenuView;

/**
 * Manager for the TimeZoneDisplay component.
 * 
 * @since May 5, 2009
 */

public class TimeZoneDisplayManager extends AbstractManager
{
  private final TimeZoneDisplayModel model;
  private final TimeZoneDisplayView view;
  private final Converter converter;
  
  //Listeners.  We only need to maintain a reference to these
  //so that we can remove them when the view is closed.
  //If we didn't do this, then the view and the manager
  //would never be release, resulting in a memory leak.
  private ActionListener convertListener;
  private ActionListener closeListener;
  private PropertyChangeListener modelChangeListener;
  private PropertyChangeListener viewChangeListener;
  
  public TimeZoneDisplayManager(TimeZoneDisplayModel model,
      TimeZoneDisplayView view,
      MenuView menuView,
      Converter converter)
  {
    super(menuView);
    
    this.model = model;
    this.view = view;
    this.converter = converter;
    
    registerListeners();
  }
  
  public void afterConstructed()
  {
    //Update time zone display info
    TimeZone timeZone = converter.getTimeZone();    
    String timeZoneName = timeZone.getDisplayName(false, TimeZone.SHORT) +
      " (" + timeZone.getID() + ")";
    view.setTimeZoneName(timeZoneName);
    
    //Update the view immediately based on the model
    whenDatePatternChanged(model.getPattern());
  }
  
  /**
   * Updates the shared model when the user clicks the convert button.
   */
  private void whenConvertButtonClicked()
  {
    try
    {
      Date utc = converter.convertFrom(view.getDateTime());
      model.setDate(utc);
    }
    catch (ParseException e)
    {
      view.showError("Invalid Date/Time");
    }
  }
  
  @Override
  protected void whenLoadClicked()
  {
    // Do the same thing as if the close button was clicked.
    // The TimeZoneGroupManger will re-add the timezone if it is one of the saved preferences.
    whenCloseButtonClicked();
  }
  
  

  @Override
  protected void whenLoadDefaultsClicked()
  {
    // Do the same thing as if the close button was clicked.
    // The TimeZoneGroupManger will re-add the timezone if it is one of the defaults.
    whenCloseButtonClicked();
  }

  /**
   * Closes the view when the close button is clicked.
   * 
   * is clicked.
   */
  private void whenCloseButtonClicked()
  {
    view.close();
    
    view.removeCloseListener(closeListener);
    view.removeConvertListener(convertListener);
    view.removePropertyChangeListener(viewChangeListener);
    model.removePropertyChangeListener(modelChangeListener);
    unregisterListeners();
  }
  
  /**
   * Updates the view when the shared model changes.
   * 
   * @param newDate
   */
  private void whenDateTimeChanged(Date newDate)
  {
    view.clearError();
    String dateString = newDate == null ? "" : converter.convertTo(newDate);
    view.setDateTime(dateString);
  }
  
  
  /**
   * Updates the converter when the date pattern changes.
   *  
   * @param newPattern
   */
  private void whenDatePatternChanged(String newPattern)
  {
    converter.setPattern(newPattern);
    whenDateTimeChanged(model.getDate());
  }
  
  private void whenUserTypesNewDateTime(String dateTime)
  {
    try 
    {
      //For right now, we don't care what the conversion is going to be
      converter.convertFrom(dateTime);
      
      //If we get here, then it is a valid dateTime.
      view.clearError();
      view.setConvertEnabled(true);
    }
    catch (ParseException e)
    {
      view.showError("Invalid Date/Time");
      view.setConvertEnabled(false);
    }    
  }
  
  /**
   * Registers this manager with the view and model events.
   */
  private void registerListeners() 
  {
    modelChangeListener = new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent event)
      {
        if(TimeZoneDisplayModel.PROP_DATE.equals(event.getPropertyName()))
        {
          whenDateTimeChanged((Date) event.getNewValue());
        }
        if(TimeZoneDisplayModel.PROP_PATTERN.equals(event.getPropertyName()))
        {
          whenDatePatternChanged((String) event.getNewValue());
        }
        
      }
    };
    model.addPropertyChangeListener(modelChangeListener);
    
    convertListener = new ActionListener() {
      public void actionPerformed(ActionEvent event)
      {
        whenConvertButtonClicked();
      }
    };
    view.addConvertListener(convertListener);
    
    closeListener = new ActionListener() {
      public void actionPerformed(ActionEvent event)
      {
        whenCloseButtonClicked();
      }
    };
    view.addCloseListener(closeListener);
    
    viewChangeListener = new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent event)
      {
       if(TimeZoneDisplayView.PROP_DATE_TIME.equals(event.getPropertyName()))
       {
         whenUserTypesNewDateTime((String) event.getNewValue());
       }
      }
    };
    view.addPropertyChangeListener(viewChangeListener);
  }

  public TimeZoneDisplayModel getModel()
  {
    return model;
  }

  public TimeZoneDisplayView getView()
  {
    return view;
  }

  public Converter getConverter()
  {
    return converter;
  }
}
